package com.travel.recommend.scheduled;

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.travel.common.utils.Query;
import com.travel.common.vo.MemberRespVo;
import com.travel.recommend.entity.DecayFunctionEntity;
import com.travel.recommend.entity.UserAttractionWeightEntity;
import com.travel.recommend.service.DecayFunctionService;
import com.travel.recommend.service.UserAttractionWeightService;
import com.travel.recommend.service.UserTagWeightService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;

import java.text.DecimalFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.stream.Collectors;

import static com.travel.common.constant.AuthServerConstant.LOGIN_USER;


@Slf4j
@Service
@EnableScheduling
public class UserAttractionAndTagWeightScheduled {

    @Autowired
    StringRedisTemplate redisTemplate;

    @Autowired
    UserAttractionWeightService userAttractionWeightService;

    @Autowired
    UserTagWeightService userTagWeightService;

    @Autowired
    DecayFunctionService decayFunctionService;

    /**
     * 每隔一天 重新计算用户对于景点的权重、用户对于标签的权重
     * 因为使用了热度衰减函数
     */
    @Scheduled(cron = "0 0 0 * * ?")
    public void updateUserAttractionAndTagWeight(){
        String s = redisTemplate.opsForValue().get(LOGIN_USER);
        MemberRespVo memberRespVo = JSON.parseObject(s,MemberRespVo.class);
        Integer userId = memberRespVo.getMemberId().intValue();

        System.out.println("用户id==="+userId);

        //1. 获取该用户有行为的所有景点
        List<UserAttractionWeightEntity> userAttractionWeightEntities = userAttractionWeightService.list(new QueryWrapper<UserAttractionWeightEntity>().eq("user_id", userId));

        System.out.println("===修改前===");
        userAttractionWeightEntities.forEach(weightEntity -> System.out.println(weightEntity.getAttractionId()+ " - "+weightEntity.getAttractionName()+" - "+weightEntity.getAttractionWeight()));

        System.out.println("===修改后===");
        //2. 加载衰减函数配置
        DecayFunctionEntity nowDecayFunction = decayFunctionService.getNowDecayFunction();
        Double init = nowDecayFunction.getInit();
        Integer m = nowDecayFunction.getM();
        Double finish = nowDecayFunction.getFinish();

        log.info("init===" + init);
        log.info("m===" + m);
        log.info("finish===" + finish);

        userAttractionWeightEntities.forEach(attraction ->{
            Double weight = attraction.getAttractionWeight();
            Date updateTime = attraction.getUpdateTime();
            int dayDiffer = 0;
            try {
                dayDiffer = getDayDiffer(updateTime, new Date());

            } catch (ParseException e) {
                e.printStackTrace();
            }

            double coefficient = exponential_decay(dayDiffer, init, m, finish);
            weight *= coefficient;
            // 对weight进行 四舍五入 保留两位小数
            DecimalFormat decimalFormat = new DecimalFormat("#.##");
            double newWeight = Double.parseDouble(decimalFormat.format(weight));
            // 更新权重和时间
            attraction.setAttractionWeight(newWeight);
            attraction.setUpdateTime(new Date());

            System.out.println(attraction.getAttractionId()+" - "+attraction.getAttractionName()+ " - "+attraction.getAttractionWeight());

            // 数据库中更新
            userAttractionWeightService.updateById(attraction);
        });

        // 同景点标签的权重累加 map：key:标签名 value:累加的权重
        Map<String, Double> collect = userAttractionWeightEntities.stream().collect(
                Collectors.groupingBy(
                        UserAttractionWeightEntity::getAttractionTag,
                        Collectors.summingDouble(UserAttractionWeightEntity::getAttractionWeight)
                )
        );

        for (Map.Entry<String, Double> entry : collect.entrySet()) {
            String tagName = entry.getKey();
            Double weight = entry.getValue();
            System.out.println("景点标签 " + tagName + " 权重 " + weight);
            userTagWeightService.updateTagWeight(userId,tagName,weight);
        }


    }

    /**
     * 热度系数衰减函数
     */
    public static double exponential_decay(double t, double init, double m, double finish) {
        double alpha = Math.log(init / finish) / m;
        double l = -Math.log(init) / alpha;
        double decay = Math.exp(-alpha * (t + l));
        return decay;
    }

    /**
     * 获取两段时间的相差的天数
     */
    public static int getDayDiffer(Date startDate, Date endDate) throws ParseException {
        //判断是否跨年
        SimpleDateFormat yearFormat = new SimpleDateFormat("yyyy");
        String startYear = yearFormat.format(startDate);
        String endYear = yearFormat.format(endDate);
        if (startYear.equals(endYear)) {
            /*  使用Calendar跨年的情况会出现问题    */
            Calendar calendar = Calendar.getInstance();
            calendar.setTime(startDate);
            int startDay = calendar.get(Calendar.DAY_OF_YEAR);
            calendar.setTime(endDate);
            int endDay = calendar.get(Calendar.DAY_OF_YEAR);
            return endDay - startDay;
        } else {
            /*  跨年不会出现问题，需要注意不满24小时情况（2016-03-18 11:59:59 和 2016-03-19 00:00:01的话差值为 0）  */
            //  只格式化日期，消除不满24小时影响
            SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
            long startDateTime = dateFormat.parse(dateFormat.format(startDate)).getTime();
            long endDateTime = dateFormat.parse(dateFormat.format(endDate)).getTime();
            return (int) ((endDateTime - startDateTime) / (1000 * 3600 * 24));
        }
    }

}
